/*-------------------------------------------------------------------------
 * drawElements Stream Library
 * ---------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Stream wrapper for deFile
 *//*--------------------------------------------------------------------*/
#include "deFileStream.h"

#include <stdlib.h>

typedef struct FileStream_s
{
	deFile*			file;
	deStreamStatus	status;
	const char*		error;
} FileStream;

static deStreamResult fileIOStream_read (deStreamData* stream, void* buf, deInt32 bufSize, deInt32* numRead)
{
	deInt64 _numRead = 0;
	FileStream* fileStream = (FileStream*)stream;


	deFileResult result = deFile_read(fileStream->file, buf, bufSize, &_numRead);
	*numRead = (deInt32)_numRead;

	switch (result)
	{
		case  DE_FILERESULT_SUCCESS:
			return DE_STREAMRESULT_SUCCESS;
			break;

		case DE_FILERESULT_ERROR:
			fileStream->error	= "deFile: DE_FILERESULT_ERROR";
			fileStream->status	= DE_STREAMSTATUS_ERROR;
			return DE_STREAMRESULT_ERROR;
			break;

		case DE_FILERESULT_END_OF_FILE:
			return DE_STREAMRESULT_END_OF_STREAM;
			break;

		default:
			fileStream->error	= "Uknown: DE_FILERESULT";
			fileStream->status	= DE_STREAMSTATUS_ERROR;
			return DE_STREAMRESULT_ERROR;
			break;
	};
}

static deStreamResult fileIOStream_write (deStreamData* stream, const void* buf, deInt32 bufSize, deInt32* numWritten)
{
	deInt64	_numWritten = 0;
	FileStream* fileStream = (FileStream*)stream;

	deFileResult result = deFile_write(fileStream->file, buf, bufSize, &_numWritten);
	*numWritten = (deInt32)_numWritten;

	switch (result)
	{
		case  DE_FILERESULT_SUCCESS:
			return DE_STREAMRESULT_SUCCESS;
			break;

		case DE_FILERESULT_ERROR:
			fileStream->error	= "deFile: DE_FILERESULT_ERROR";
			fileStream->status	= DE_STREAMSTATUS_ERROR;
			return DE_STREAMRESULT_ERROR;
			break;

		case DE_FILERESULT_END_OF_FILE:
			return DE_STREAMRESULT_END_OF_STREAM;
			break;

		default:
			fileStream->error	= "Uknown: DE_FILERESULT";
			fileStream->status	= DE_STREAMSTATUS_ERROR;
			return DE_STREAMRESULT_ERROR;
			break;
	};
}

static const char* fileIOStream_getError (deStreamData* stream)
{
	FileStream* fileStream = (FileStream*)stream;
	/* \note [mika] There is only error reporting through return value in deFile */
	return fileStream->error;
}

static deStreamResult fileIOStream_flush (deStreamData* stream)
{
	/* \todo mika deFile doesn't have flush, how should this be handled? */
	DE_UNREF(stream);

	return DE_STREAMRESULT_SUCCESS;
}

static deStreamResult fileIOStream_deinit (deStreamData* stream)
{
	FileStream* fileStream = (FileStream*)stream;

	deFile_destroy(fileStream->file);

	free(fileStream);

	return DE_STREAMRESULT_SUCCESS;
}

static deStreamStatus fileIOStrem_getStatus (deStreamData* stream)
{
	FileStream* fileStream = (FileStream*)stream;
	return fileStream->status;
}

static const deIOStreamVFTable fileIOStreamVFTable = {
	fileIOStream_read,
	fileIOStream_write,
	fileIOStream_getError,
	fileIOStream_flush,
	fileIOStream_deinit,
	fileIOStrem_getStatus
};

static const deIOStreamVFTable fileInStreamVFTable = {
	fileIOStream_read,
	DE_NULL,
	fileIOStream_getError,
	DE_NULL,
	fileIOStream_deinit,
	fileIOStrem_getStatus
};

static const deIOStreamVFTable fileOutStreamVFTable = {
	DE_NULL,
	fileIOStream_write,
	fileIOStream_getError,
	fileIOStream_flush,
	fileIOStream_deinit,
	fileIOStrem_getStatus
};

void fileIOStream_init (deIOStream* stream, const char* filename, deFileMode mode)
{
	FileStream* fileStream = DE_NULL;

	DE_ASSERT(stream);

	fileStream = malloc(sizeof(FileStream));

	/* \note mika Check that file is readable and writeable, currently not supported by deFile */
	stream->vfTable		= &fileIOStreamVFTable;
	stream->streamData	= (deStreamData*)fileStream;

	fileStream->file	= deFile_create(filename, mode);
	fileStream->status	= DE_STREAMSTATUS_GOOD;
	fileStream->error	= DE_NULL;

	if (!fileStream->file)
		fileStream->status = DE_STREAMSTATUS_ERROR;
}

void deFileInStream_init (deInStream* stream, const char* filename, deFileMode mode)
{
	FileStream* fileStream = DE_NULL;

	DE_ASSERT(stream);

	fileStream = malloc(sizeof(FileStream));

	/* \note mika Check that file is readable, currently not supported by deFile */
	stream->ioStream.vfTable		= &fileInStreamVFTable;
	stream->ioStream.streamData		= (deStreamData*)fileStream;

	fileStream->file	= deFile_create(filename, mode);
	fileStream->status	= DE_STREAMSTATUS_GOOD;
	fileStream->error	= DE_NULL;

	if (!fileStream->file)
		fileStream->status = DE_STREAMSTATUS_ERROR;
}

void deFileOutStream_init (deOutStream* stream, const char* filename, deFileMode mode)
{
	FileStream* fileStream = DE_NULL;

	DE_ASSERT(stream);

	fileStream = malloc(sizeof(FileStream));

	/* \note mika Check that file is writeable, currently not supported by deFile */
	stream->ioStream.vfTable		= &fileOutStreamVFTable;
	stream->ioStream.streamData		= (deStreamData*)fileStream;

	fileStream->file	= deFile_create(filename, mode);
	fileStream->status	= DE_STREAMSTATUS_GOOD;
	fileStream->error	= DE_NULL;

	if (!fileStream->file)
		fileStream->status = DE_STREAMSTATUS_ERROR;;
}
